package processes

import (
	"chatroom/common/message"
	"chatroom/server/model"
	"chatroom/server/utils"
	"encoding/json"
	"fmt"
	"net"
)

type UserProcess struct {
	//分析字段
	Conn net.Conn
	//增加一个字段，表示该conn是那个用户的
	UserId int
}

//离线，删除 map[int]*UserProcess 表中用户
func NotifyOthersOfflineUser(conn net.Conn) {

	for id1, up1 := range userMgr.onlineUsers {
		if conn == up1.Conn { //找出下线那个用户
			//这里编写通知所有在线用户的方法

			//这个 userid 要通知其他在线用户自己下线
			//遍历 onlineUsers，然后一个一个发送
			for id2, up2 := range userMgr.onlineUsers {
				//过滤掉自己
				if id1 == id2 {
					continue
				}
				//开始通知 {单独写一个方法}
				up2.NotifyMeOfflineUser(id1)
			}

			userMgr.DelOnlineUser(id1)
		}
	}
}

//发送给客户端  userid已下线
func (userProcess *UserProcess) NotifyMeOfflineUser(userId int) {
		//组装我们的 NotifyUserStatusMes
		var mes message.Message
		mes.Type = message.NotifyUserStatusMesType
	
		var notifyUserStatusMes message.NotifyUserStatusMes
		notifyUserStatusMes.UserId = userId
		notifyUserStatusMes.Status = message.UserOffline
	
		//将 NotifyUserStatusMes 序列化
		data, err := json.Marshal(notifyUserStatusMes)
		if err != nil {
			fmt.Println("NotifyMeOfflineUser json.Marshal err=", err)
			return
		}
		//将序列化后的 notifyUserStatusMes 赋值给Data
		mes.Data = string(data)
	
		//对 mes 再次序列化，准备发送
		data, err = json.Marshal(mes)
		if err != nil {
			fmt.Println("NotifyMeOfflineUser json.Marshal err=", err)
			return
		}
	
		//发送，创建TransFer实例发送
		tf := &utils.TransFer{
			Conn: userProcess.Conn,
		}
		err = tf.WritePkg(data)
		if err != nil {
			fmt.Println("NotifyMeOfflineUser tf.WritePkg(data) err=", err)
			return
		}
}

//这里编写通知所有在线用户的方法
//这个userid 要通知其他在线用户自己上线
func (userProcess *UserProcess) NotifyOthersOnlineUser(userId int) {

	//遍历 onlineUsers，然后一个一个发送
	for id, up := range userMgr.onlineUsers {
		//过滤掉自己
		if id == userId {
			continue
		}
		//开始通知 {单独写一个方法}
		up.NotifyMeOnlineUser(userId)
	}

}

//通知上线
func (userProcess *UserProcess) NotifyMeOnlineUser(userId int) {

	//组装我们的 NotifyUserStatusMes
	var mes message.Message
	mes.Type = message.NotifyUserStatusMesType

	var notifyUserStatusMes message.NotifyUserStatusMes
	notifyUserStatusMes.UserId = userId
	notifyUserStatusMes.Status = message.UserOnline

	//将 NotifyUserStatusMes 序列化
	data, err := json.Marshal(notifyUserStatusMes)
	if err != nil {
		fmt.Println("json.Marshal err=", err)
		return
	}
	//将序列化后的 notifyUserStatusMes 赋值给Data
	mes.Data = string(data)

	//对 mes 再次序列化，准备发送
	data, err = json.Marshal(mes)
	if err != nil {
		fmt.Println("json.Marshal err=", err)
		return
	}

	//发送，创建TransFer实例发送
	tf := &utils.TransFer{
		Conn: userProcess.Conn,
	}
	err = tf.WritePkg(data)
	if err != nil {
		fmt.Println("NotifyUserStatusMes err=", err)
		return
	}

}

//注册
func (userProcess *UserProcess) ServerProcessRegister(mes *message.Message) (err error) {

	//1.先从mes中 取出mes.Data ,并直接发序列化成RegisterMes
	//fmt.Printf("服务端接收消息ms的长度=%d 内容是=%s", len(mes.Data), string(mes.Data))

	var registerMes message.RegisterMes
	err = json.Unmarshal([]byte(mes.Data), &registerMes)
	if err != nil {
		fmt.Println("json.Umarshal fail error=", err)
		return
	}

	//1.先声明一个 resMes,返回注册信息
	var resMes message.Message
	resMes.Type = message.RegisterResMesType
	var registerResMes message.RegisterResMes

	//我们需要到redis 数据库去完成验证
	//1.使用model.MyUserDao 到 redis 去验证
	err = model.MyUserDao.Register(&registerMes.User)
	if err != nil {
		if err == model.ERROR_USER_EXISTS {
			registerResMes.Code = 400 //用户存在
			registerResMes.Error = err.Error()
		} else {
			registerResMes.Code = 506
			registerResMes.Error = "注册发送未知错误"
		}
	} else {
		registerResMes.Code = 200 //用户注册成功
	}

	//3.将 registerResMes 序列化
	data, err := json.Marshal(registerResMes)
	if err != nil {
		fmt.Println("json.Marshal fail error=", err)
		return
	}

	//4.将 data 赋值给 ResMes
	resMes.Data = string(data)

	//5.对 ResMes 进行序列化，准备发送
	data, err = json.Marshal(resMes)
	if err != nil {
		fmt.Println("json.Marshal fail error=", err)
		return
	}

	//6.发送 data，我们将其封装到writePkg中
	//因为使用分层模式(mvc), 我们先创建一个 TransFer实例，然后读取
	tf := &utils.TransFer{
		Conn: userProcess.Conn,
	}
	err = tf.WritePkg(data)
	if err != nil {
		fmt.Println("wtitePkg fail error=", err)
		return
	}

	return

}

//登录
func (userProcess *UserProcess) ServerProcessLogin(mes *message.Message) (err error) {
	//核心代码...
	//1.先从mes中 取出mes.Data ,并直接发序列化成LoginMes
	var loginMes message.LoginMes
	err = json.Unmarshal([]byte(mes.Data), &loginMes)
	if err != nil {
		fmt.Println("json.Umarshal fail error=", err)
		return
	}

	//1.先声明一个 resMes
	var resMes message.Message
	resMes.Type = message.LoginResMesType

	//2.再声明一个 loginResMes, 并完成赋值
	var loginResMes message.LoginResMes

	//我们需要到redis 数据库去完成验证
	//1.使用model.MyUserDao 到 redis 去验证
	user, err := model.MyUserDao.Login(loginMes.UserId, loginMes.UserPwd)
	if err != nil {

		if err == model.ERROR_USER_NOTEXISTS {
			loginResMes.Code = 500 // 500 状态码，表示用户不存在
			loginResMes.Error = err.Error()
		} else if err == model.ERROR_USER_PWD {
			loginResMes.Code = 300 // 300 状态码，表示密码不存在
			loginResMes.Error = err.Error()
		} else {
			loginResMes.Code = 505 // 505状态码，表示未知错误信息
			loginResMes.Error = "服务器内部错误..."
		}

		//这里我们先测试成功，然后再根据返回具体错误信息

	} else {
		//合法
		loginResMes.Code = 200
		//这里，因为用户登陆成功，我们把该登录成功的 放到 userMgr 中
		//将登录成功的用户的userid 赋给 userProcess
		userProcess.UserId = loginMes.UserId
		userMgr.AddOnlineUser(userProcess)
		//通知其他的在线的用户，我上线了
		userProcess.NotifyOthersOnlineUser(loginMes.UserId)

		//将当前在线用户的id ，放入到 loginResMes.UsersId
		//遍历 userMgr.onlineusers
		for id, _ := range userMgr.onlineUsers {
			loginResMes.UsersId = append(loginResMes.UsersId, id)
		}

		fmt.Println(user, "登录成功")
	}

	//3.将 loginResMes 序列化
	data, err := json.Marshal(loginResMes)
	if err != nil {
		fmt.Println("json.Marshal fail error=", err)
		return
	}

	//4.将 data 赋值给 ResMes
	resMes.Data = string(data)

	//5.对 ResMes 进行序列化，准备发送
	data, err = json.Marshal(resMes)
	if err != nil {
		fmt.Println("json.Marshal fail error=", err)
		return
	}

	//6.发送 data，我们将其封装到writePkg中
	//因为使用分层模式(mvc), 我们先创建一个 TransFer实例，然后读取
	tf := &utils.TransFer{
		Conn: userProcess.Conn,
	}
	err = tf.WritePkg(data)
	if err != nil {
		fmt.Println("ServerProcessLogin wtitePkg fail error=", err)
		return
	}

	return
}
